/**
 * Player Entity
 */
game.PlayerEntity = me.Entity.extend({

    /**
     * constructor
     */
    init:function (x, y, settings) {
        // call the constructor
        this._super(me.Entity, 'init', [x, y, settings]);

        // variables
        // -----------
        this.health = 5;
        this.fireCoolDown = 0;

        // melonjs settings
        // ----------------
        // set the default horizontal & vertical speed (accel vector)
        this.body.setVelocity(3, 3);
        // set the display to follow our position on both axis
        me.game.viewport.follow(this.pos, me.game.viewport.AXIS.BOTH);
        // ensure the player is updated even when outside of the viewport
        this.alwaysUpdate = true;

        // animations
        // -----------
        this.renderable.addAnimation("walkhorizontal", [0, 1]);
        this.renderable.addAnimation("walkup", [2,3]);
        this.renderable.addAnimation("walkdown", [4,5]);
        this.renderable.addAnimation("stand", [0]);
        this.renderable.addAnimation("dead", [6]);
        // set the standing animation as default
        this.renderable.setCurrentAnimation("stand");

    },

    /**
     * update the entity
     */
    update : function (dt) {

        if (this.alive) {
            var left = me.input.keyStatus("left"),
                right = me.input.keyStatus("right"),
                up = me.input.keyStatus("up"),
                down = me.input.keyStatus("down"),
                fireKey = me.input.keyStatus("fireKey"),
                fire = me.input.keyStatus("fire") || fireKey;

            if (left) {
                // flip the sprite on horizontal axis
                this.renderable.flipX(true);
                // update the entity velocity
                this.body.vel.x -= this.body.accel.x * me.timer.tick;
                // change to the walking animation
                if (!this.renderable.isCurrentAnimation("walkhorizontal")) {
                    this.renderable.setCurrentAnimation("walkhorizontal");
                }
            } else if (right) {
                // unflip the sprite
                this.renderable.flipX(false);
                // update the entity velocity
                this.body.vel.x += this.body.accel.x * me.timer.tick;
                // change to the walking animation
                if (!this.renderable.isCurrentAnimation("walkhorizontal")) {
                    this.renderable.setCurrentAnimation("walkhorizontal");
                }
            }

            if (up) {
                // update the entity velocity
                this.body.vel.y -= this.body.accel.y * me.timer.tick;
                // change to the walking animation
                if (!this.renderable.isCurrentAnimation("walkup")) {
                    this.renderable.setCurrentAnimation("walkup");
                }
            } else if (down) {
                // update the entity velocity
                this.body.vel.y += this.body.accel.y * me.timer.tick;
                // change to the walking animation
                if (!this.renderable.isCurrentAnimation("walkdown")) {
                    this.renderable.setCurrentAnimation("walkdown");
                }
            }

            if (!(left || right)) {
                this.body.vel.x = 0;
            }
            if (!( down || up)) {
                this.body.vel.y = 0;
            }
            if (!(left || right || up || down)) {
                // change to the standing animation
                this.renderable.setCurrentAnimation("stand");
            }

            if (fire && this.fireCoolDown == 0) {
                me.audio.play("M16_single", false);

                var vector;
                if (fireKey) {
                    vector = new me.Vector2d(
                        (this.renderable.lastflipX ? -16 : 9999),
                        this.pos.y);
                }
                else {
                    var mouseX = me.input.mouse.pos.x,
                        mouseY = me.input.mouse.pos.y;
                    log("Mouse " + format0(mouseX) + " " + format0(mouseY));
                    vector = me.game.viewport.localToWorld(mouseX, mouseY);
                }
                this.fireCoolDown = 5;
                var bullet = new game.BulletEntity(this.pos.x, this.pos.y, {
                    target : vector
                });
                me.game.world.addChild(bullet, 350);
            }
            else if (this.fireCoolDown > 0) {
                this.fireCoolDown--;
            }

            if (this.health <= 0) {
                this.alive = 0;
                this.renderable.setCurrentAnimation("dead");
                this.renderable.flipX(Math.random() <= 0.5);
            }

            // apply physics to the body (this moves the entity)
            this.body.update(dt);
            // handle collisions against other shapes
            me.collision.check(this);
            // return true if we moved or if the renderable was updated
            return (this._super(me.Entity, 'update', [dt]) || this.body.vel.x !== 0 || this.body.vel.y !== 0);
        }
        return false;
    },

    /**
     * colision handler
     * (called when colliding with other objects)
     */
    onCollision : function (response, other) {
        if (this.alive) {
            switch (response.b.body.collisionType) {
                case me.collision.types.WORLD_SHAPE:
                    break;
                case me.collision.types.ENEMY_OBJECT:
                    switch (other.name.toLowerCase()) {
                        case "enemyentity":
                            if (other.alive) {
                                // enemy stabbed to death instantly, but costs 1 hp
                                this.renderable.flicker(750);
                                this.health--;
                                return true;
                            }
                            return false; // walk over corpses
                        case "bullet":
                            return false; // ignore bullets
                    }
                    break;
                default:
                    return false;
            }

            // Make the object solid
            return true;
        }
        return false;
    }
});

/*---------------------------
 Bullet entity
 ----------------------------*/
game.BulletEntity = me.Entity.extend({
    init: function(x, y, settings) {

        var w = 8,
            h = w;
        settings.image = "projectiles";
        settings.name = "bullet";
        settings.spritewidth = settings.width = w;
        settings.spriteheight = settings.height = h;

        // call the parent constructor
        this._super(me.Entity, 'init', [x, y , settings]);

        this.body.addShape(new me.Ellipse(17, 17, w, h)); // offset to middle of player sprite (34/2)

        this.renderable.addAnimation("bullet", [0]);
        this.renderable.addAnimation("bulletHit", [1]);
        this.renderable.setCurrentAnimation("bullet");

        // Set target
        this.target = settings["target"];

        // manually update the entity bounds as we manually change the position
        this.updateBounds();
//        this.body.setVelocity(6, 6);
    },
    update: function(dt) {
        // move towards target then remove bullet.
        var angle = this.angleToPoint(new me.Vector2d(this.target.x, this.target.y));
        this.body.vel.set(Math.cos(angle) * 6, Math.sin(angle) * 6);


        if (Math.abs(this.distanceToPoint(this.target) - 17) < 5) {
            log("Bullet hit @ " + format0(this.pos.x) + " " + format0(this.pos.y) +
                ". Distance to dest. " + Math.floor(Math.abs(this.distanceToPoint(this.target))));
            me.game.world.removeChild(this);
        }
//        this.goRight = this.pos.x < this.target.x;
//        this.goDown = this.pos.y < this.target.y;
//        this.body.vel.x += (this.goRight) ? this.body.accel.x * me.timer.tick : -this.body.accel.x * me.timer.tick;
//        this.body.vel.y += (this.goDown) ? this.body.accel.y * me.timer.tick : -this.body.accel.y * me.timer.tick;
        // update the body movement
        this.body.update(dt);
        // handle collisions against other shapes
        me.collision.check(this);
        // return true if we moved or if the renderable was updated
        return (this._super(me.Entity, 'update', [dt]) || this.body.vel.x !== 0 || this.body.vel.y !== 0);
    },
    onCollision : function (response, other) {
        if (!this.alive) {
            this.body.setCollisionMask(me.collision.types.NO_OBJECT);
        }
        switch (response.b.body.collisionType) {
            case me.collision.types.WORLD_SHAPE:
                break;
            case me.collision.types.ENEMY_OBJECT:
                switch (other.name.toLowerCase()) {
                    case "mainplayer":
                    case "bullet":
                        return false;
                    case "enemyentity":
                        if (!other.alive) {
                            return false;
                        }
                        break;
                }
                break;
        }
        if (this.alive) { // allows for one frame of bullet hit animation
            this.renderable.setCurrentAnimation("bulletHit");
            log("Bullet hit " + other.name);
            this.alive = false;
        }
        else {
            me.game.world.removeChild(this);
        }
        return true;
    }
});

/* --------------------------
 an enemy Entity
 ------------------------ */
game.EnemyEntity = me.Entity.extend({
    init: function(x, y, settings) {

        // call the parent constructor
        this._super(me.Entity, 'init', [x, y , settings]);

        this.mode = settings["mode"];
        this.health = 5;
        this.goRight = false;
        this.goDown = false;

        this.renderable.addAnimation("walkhorizontal", [0, 1]);
        this.renderable.addAnimation("walkup", [2,3]);
        this.renderable.addAnimation("walkdown", [4,5]);
        this.renderable.addAnimation("stand", [0]);
        this.renderable.addAnimation("dead", [6]);
        // set the standing animation as default
        this.renderable.setCurrentAnimation("stand");

        this.body.setVelocity(1, 1);
    },

    // manage the enemy movement
    update: function(dt) {

        if (this.alive) {

            if (this.mode == "active") {
                // follow player if too far away to shoot
                this.endX = getPlayerPos().x;
                this.endY = getPlayerPos().y;
                var dist = this.distanceTo(getPlayer());
                // in range to fire at player?
                this.fireEnabled = dist < 200;
                // too close to player, will not move further
                if (dist < 100) {
                    this.endX = this.pos.x;
                    this.endY = this.pos.y;
                    return;
                }
            }

            this.goRight = this.pos.x < this.endX;
            this.goDown = this.pos.y < this.endY;
            if (Math.abs(this.pos.x - this.endX) > Math.abs(this.pos.y - this.endY)) {
                if (!this.renderable.isCurrentAnimation("walkhorizontal")) {
                    this.renderable.setCurrentAnimation("walkhorizontal");
                    this.renderable.flipX(!this.goRight);
                }
            }
            else if (this.goDown) {
                if (!this.renderable.isCurrentAnimation("walkdown")) {
                    this.renderable.setCurrentAnimation("walkdown");
                }
            }
            else {
                if (!this.renderable.isCurrentAnimation("walkup")) {
                    this.renderable.setCurrentAnimation("walkup");
                }
            }
            this.body.vel.x += (this.goRight) ? this.body.accel.x * me.timer.tick : -this.body.accel.x * me.timer.tick;
            this.body.vel.y += (this.goDown) ? this.body.accel.y * me.timer.tick : -this.body.accel.y * me.timer.tick;
            if (this.pos.x == this.endX && this.pos.y == this.endY) {
                this.body.vel.x = this.body.vel.x = 0;
                this.renderable.setCurrentAnimation("stand");
            }
            // update the body movement
            this.body.update(dt);

            // handle collisions against other shapes
            me.collision.check(this);

            // return true if we moved or if the renderable was updated
            return (this._super(me.Entity, 'update', [dt]) || this.body.vel.x !== 0 || this.body.vel.y !== 0);
        }
        return false;
    },

    /**
     * colision handler
     * (called when colliding with other objects)
     */
    onCollision : function (response, other) {
        if (this.alive) {
            switch (response.b.body.collisionType) {
                case me.collision.types.WORLD_SHAPE:
                    return true;
                case me.collision.types.ENEMY_OBJECT:
                    // player stabs enemy to death if they collide
                    switch (other.name.toLowerCase()) {
                        case "mainplayer":
                            this.health -= 2;
                            break;
                        case "bullet":
                            this.health--;
                            break;
                    }
            }
            if (this.health < 1) {
                this.alive = false;
                this.renderable.setCurrentAnimation("dead");
                this.renderable.flipX(Math.random() <= 0.5);
            }
            return true;
        }
        return false;
    }
});

var c = 0;
function log(txt) {
    console.log(txt);

    if (c++ > 20) {
        console.clear();
        c = 0;
    }
}

function format0(number) {
    return Math.floor(number);
}

var player = null, playerPos = null;
function getPlayerPos() {
    if (playerPos) {
        return playerPos;
    }
    player = me.game.world.getChildByName("mainPlayer")[0];
    if (player) {
        playerPos = player.getBounds().pos;
        return playerPos;
    }
    throw new Exception("mainPlayer not found at this time");
}

function getPlayer() {
    return player || me.game.world.getChildByName("mainPlayer")[0];
}
